#!/usr/bin/python

import random
import time
import sys
import getopt
import socket
import re
import hashlib
import struct

DELAY = .1  # Time between random numbers, in seconds
PAUSE = 3   # Time between batches of numbers, in seconds

def usage():
    print "Usage: ./dummy-client.py [-p port] [-h host] [-do]"
    print "-o Turns on 'offline mode' (no host/port needed)"
    print "-d Turns on 'debug mode'"

def hash(str):
    m = hashlib.sha1()
    m.update(str)
    return m.hexdigest()

def serialize(type, pubid, seq, data):
    ret = struct.pack('!B', type)
    ret += struct.pack('!40s', pubid)
    ret += struct.pack('!q', seq)
    ret += struct.pack('!' + str(len(data)) + 's', data)
    return ret

def deserialize(buf):
    buflen = len(buf)
    datalen = buflen - 49
    data = dict()
    if (buflen < 50):
        return data
    data['type'] = struct.unpack('!B', buf[0])[0]
    data['pubid'] = struct.unpack('!40s', buf[1:41])[0]
    data['seq'] = struct.unpack('!q', buf[41:49])[0]
    data['data'] = struct.unpack('!' + str(datalen) + 's', buf[49:])[0]

    return data

def create_pub(pubid, seq, data):
    return serialize(0x01, pubid, seq, data)

def create_sub(pubid, seq, data):
    return serialize(0x00, pubid, seq, data)

def subscribe(myid, otherid):
    pubid = hash(myid)
    data = hash(otherid)
    print "Subscribing to '%s' (%s)..." % (otherid, data)
    seq = 0
    try:
        if not offline:
            s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            s.connect((host,port))
            s.send(create_sub(pubid, seq, str(data)))
            while True:
                msg, addr = s.recvfrom(2048)
                msg = deserialize(msg)
                if 'data' in msg:
                    print msg['data']
    except KeyboardInterrupt:
        if not offline:
            s.close()
        print ""

    return

def publish(id):
    pubid = hash(id)
    seq = 0
    try:
        i = 0
        if not offline:
            s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            s.connect((host,port))
        while True:
            data = random.random()
            if not offline:
                seq += 1
                s.send(create_pub(pubid, seq, str(data)))
            print data
            i += 1
            i %= 10
            if i == 0:
                time.sleep(PAUSE)
            else:
                time.sleep(DELAY)
    except KeyboardInterrupt:
        if not offline:
            s.close()
        print ""

def publish_file(id):
    pubid = hash(id)
    seq = 0 
    try:
        i = 0 
        if not offline:
            s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            s.connect((host,port))
        while True:
            data = raw_input("").strip()
            if not offline:
                seq += 1
                s.send(create_pub(pubid, seq, str(data)))
            time.sleep(random.random() / 10)
    except KeyboardInterrupt:
        if not offline:
            s.close()
        print ""
    except EOFError:
        if not offline:
            s.close()
        print ""

if __name__ == "__main__":
    try:
        opts, args = getopt.getopt(sys.argv[1:], "odh:p:")
    except getopt.GetoptError, err:
        usage()
        exit(-2)

    port = 0
    host = ''
    offline = False
    for o,v in opts:
        if o == "-p":
            port = int(v)
        elif o == "-h":
            host = v
        elif o == "-d":
            print "Debug enabled."
        elif o == "-o":
            print "Offline enabled."
            offline = True

    if (port == 0 or host == '') and not offline:
        usage()
        exit(-1)

    sub_re = re.compile('^sub ([a-zA-Z0-9]+) ([a-zA-Z0-9]+)$')
    pub_re = re.compile('^pub ([a-zA-Z0-9]+)$')    
    pubf_re = re.compile('^pubfile ([a-zA-Z0-9]+)$')

    # main command input loop
    while True:
        try:
            cmd = raw_input("> ").strip()
        except KeyboardInterrupt:
            print ""
            break
        except EOFError:
            print ""
            break

        if cmd == "exit" or cmd == "quit":
            break
        elif pubf_re.search(cmd):
            match = pubf_re.search(cmd)
            publish_file(match.group(1))
        elif pub_re.search(cmd):
            match = pub_re.search(cmd)
            publish(match.group(1))
        elif cmd == "pub":
            print "'pub' requires a publisher id"
        elif sub_re.search(cmd):
            match = sub_re.search(cmd)
            subscribe(match.group(1), match.group(2))
        elif cmd == "sub":
            print "'sub' requires a publisher id"
        elif cmd == "help":
            print "exit           - Ends the program."
            print "help           - Shows this help message."
            print "pub pubid      - Begins publishing random data."
            print "quit           - Alias for 'exit'."
            print "sub myid pubid - Subscribes to publishing id 'pubid'."
        else:
            print "Unknown command, try 'help'"

    print "Client done."
